package model.invoicing;

import java.sql.SQLException;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import enums.Property;
import static model.Configuration.*;
import model.Configuration;
import model.Control;
import model.Utility;
import model.data.tableModel.inventory.Inventory;
import model.data.tableModel.person.Customer;
import model.data.tableModel.person.Staff;
import model.data.tableModel.sale.Bill;
import model.data.wrapper.InputWrapper;
import model.handler.databaseHandling.QueryFactory;
import model.handler.fileHandling.FileHandler;
import model.invoicing.DateHandler.InvoiceDate;

/**
 * Utility class for invoicing.
 * <p>
 * This Utility class musn't be instantiated it contains only static methods that are needed for creating the LaTeX code for the invoicing.
 * Creates all LaTeX code for the different invoices that are created.
 */
public final class InvoiceHandler {

	private final static Logger debug = LogManager.getLogger("DEBUG");

	/**
	 * Musn't be invoked because this class is a utility class.
	 */
	private InvoiceHandler() {
		throw new AssertionError("InvoiceHandler is a utility class and musn't be invoked!");
	}

	/**
	 * Formats the given value to a decimal number with two digits after the comma.
	 * @param value
	 * @return formatted value
	 */
	public static String formatValueToInvoice(double value) {
		NumberFormat formatter = new DecimalFormat("###0.00");
		return formatter.format(value);
	}
	
	/**
	 * @param c Reference to the customer which should be invoiced
	 * @param operator Reference to the current operator of the shop
	 * @param invoiceDate Date of the invoice
	 * @return The String containing the LaTeX-Code for the invoice of the
	 *         specified Customer
	 * @throws SQLException 
	 */
	public static String createInvoiceCustomer(Customer c, Staff operator, Date invoiceDate) throws SQLException {

		int billID = QueryFactory.getBillID(c.getCustomerID(), DateHandler.formatMonthDateToString(invoiceDate));
		List<Integer> purchaseIDs = QueryFactory.getPurchaseIDs(billID);

		String tex = createTexInvoice(purchaseIDs, billID, operator);
		
		debug.debug("LaTeX invoice of customer " + c.getName() + " created.");

		return tex;
	}
	
	/**
	 * Returns the LaTeX code for an inventory invoice.
	 * @param inputList List with all input from the view
	 * @param control reference to the control class in order to invoke some methods
	 * @param inventory The inventory object which will be written to the database
	 * @return
	 */
	public static String createInventoryInvoice(List<List<InputWrapper>> inputList, Control control, Inventory inventory){
		String headerDate = DateHandler.formatHeaderDate(DateHandler.parseMonthDateStringToDate(inventory.getDate()));
		String tex = createInventoryInvoiceBegin(headerDate);
		StringBuilder sb = new StringBuilder();
		sb.append(tex);

		double loss = control.computeLossEachProduct(sb, inputList);

		String endtex = createInventoryInvoiceEnd(loss);
		sb.append(endtex);
		if (inventory.getComment() != null) {
			sb.append("\\");
			sb.append("\\Kommentar: \\");
			sb.append("\\");
			sb.append(inventory.getComment());
		}
		sb.append("\\end{document}");
		
		debug.debug("LaTeX code of inventory invoice created");
		return sb.toString();
	}
	
	/**
	 * Returns a String containing the LaTeX code of one product that should be added to an inventory table.
	 * @param isStock
	 * @param stock
	 * @param productName
	 * @param cost
	 * @param singleLoss
	 * @return
	 */
	public static String getInventoryProduct(int isStock, int stock, String productName, double cost, double singleLoss){
		String str = Utility.concatStrings(productName, "&",formatValueToInvoice(cost), "\\euro","&", String.valueOf(stock), "&",String.valueOf(isStock),  "&", String.valueOf(isStock-stock), "&",formatValueToInvoice(singleLoss), "\\", "\\");
		return str;
	}

	/**
	 * @return the String containing the LaTeX-Code for the salary invoice of the staff.
	 */
	public static String createSalaryInvoice(Date invDate) {
		StringBuilder sb = new StringBuilder();
		String str = FileHandler.readFromFile(Configuration.TEXLOHNHEADER);
		
		str = str.replaceFirst("date", DateHandler.formatHeaderDate(invDate));
		sb.append(str);

		Date invoiceDate = DateHandler.getMonthBeforeDate(new Date());
		Map<Staff, Integer> workingStaffList = QueryFactory.getStaffWorking(DateHandler.getDayFromDate(invoiceDate,InvoiceDate.FIRSTDAY), DateHandler.getDayFromDate(invoiceDate,InvoiceDate.LASTDAY));

		for (Staff s : workingStaffList.keySet()) {
			int salesdayCount = workingStaffList.get(s);
			String salary = QueryFactory.getProperty(Property.SALARY);
			
			String table = FileHandler.readFromFile(Configuration.TEXTABLELOHN);
			
			table = table.replaceAll("empf", s.getPerson().toString());
			table = table.replaceFirst("NumberSalesdays", String.valueOf(salesdayCount));
			table = table.replaceFirst("salary", formatValueToInvoice(Double.valueOf(salary)));
			table = table.replaceAll("total", String.valueOf(formatValueToInvoice(Double.valueOf(Integer.valueOf(salary) * salesdayCount))));

			sb.append(table);
			
			String signature = FileHandler.readFromFile(Configuration.TEXNORMALSIGNATURE);
			signature = signature.replaceFirst("empf", s.getPerson().toString());
			signature = signature.replaceFirst("Betrag", "Gehalt");
			
			sb.append(signature);
		}

		sb.append("\\end{document}");

		return sb.toString();
	}

	/**
	 * @param billIDs
	 * @param operator
	 * @return the String containing the LaTeX code for the monthly balance invoice.
	 */
	public static String createTexBalanceInvoice(List<Integer> billIDs, Staff operator) {
		StringBuilder sb = new StringBuilder();

		List<Integer> purchaseIDs = new ArrayList<>();
		for (int i : billIDs) {
			purchaseIDs.addAll(QueryFactory.getPurchaseIDs(i));
		}

		sb.append(createBalanceTotalPurchase(billIDs));

		sb.append("\\subsection*{Gewinn}");
		
		
		String profitTable = FileHandler.readFromFile(Configuration.TEXTABLEBEGIN);
		profitTable = profitTable.replaceFirst("Einzelpreis", "Marge");
		profitTable = profitTable.replaceFirst("Gesamtpreis", "Gewinn");
		sb.append(profitTable);
		sb.append(QueryFactory.getTexProfit(purchaseIDs));
		sb.append(createTexSalaryOperator(QueryFactory.getTotalProfit(DateHandler.getMonthBeforeDate(new Date()))-QueryFactory.getTotalSalary(DateHandler.getMonthBeforeDate(new Date()))));

		String signature = FileHandler.readFromFile(Configuration.TEXNORMALSIGNATURE);
		String opName = operator.getPerson().getFirstName() + " " + operator.getPerson().getLastName() + " (" + operator.getPerson().getRoom() + ")";
		signature = signature.replaceFirst("empf", opName);
		signature = signature.replaceFirst("Betrag", "Gehalt");

		String signatureOpt = FileHandler.readFromFile(Configuration.TEXNORMALSIGNATURE);
		signatureOpt = signatureOpt.replaceFirst("empf", opName);
		signatureOpt = signatureOpt.replaceFirst("Betrag", "Optionale Gewinnbeteiligung");

		sb.append(signature);
		sb.append(signatureOpt);

		String strPureProfit = createTexPureProfit(purchaseIDs);
		sb.append(strPureProfit);

		sb.append("\\end{document}");
		
		debug.debug("LaTeX balance invoice created.");
		return sb.toString();
	}
	
	/*
	 * HELPER METHODS BELOW
	 */
	
	/**
	 * Returns the String containing the LaTeX code for the beginning of the inventory invoice.
	 * @param date
	 * @return
	 */
	private static String createInventoryInvoiceBegin(String date){
		StringBuilder sb = new StringBuilder();
		String header = FileHandler.readFromFile(Configuration.TEXBALANCEHEADER);
		header = header.replaceFirst("Monatsbilanz", "Inventur");
		header = header.replaceFirst("date", date);
		sb.append(header);
		String tableBegin = FileHandler.readFromFile(Configuration.TEXINVENTORYTABLEBEGIN);
		sb.append(tableBegin);
		
		return sb.toString();
	}

	/**
	 * @param purchaseIDs
	 * @param billID
	 * @param operator
	 * @return The String containing the LaTeX code for the monthly invoice of a customer
	 * @throws SQLException 
	 */
	private static String createTexInvoice(List<Integer> purchaseIDs, int billID, Staff operator) throws SQLException {
		StringBuilder sb = new StringBuilder();
		
		String header = createTexInvoiceHeader(billID);
		String mid = createTexInvoiceMid(purchaseIDs);
		String end = createTexInvoiceEnd(operator);
		String attachement = createTexInvoiceAttachement(purchaseIDs);

		sb.append(header);
		sb.append(mid);
		sb.append(end);
		sb.append(attachement);

		return sb.toString();
	}

	/**
	 * @param purchaseIDs
	 * @return The String containing the LaTeX code for the attachement containing all purchases of the customer
	 */
	private static String createTexInvoiceAttachement(List<Integer> purchaseIDs) {
		StringBuilder sb = new StringBuilder();
		
		sb.append("\\newpage\\hrule");
		sb.append("\\section*{Auff\\");
		sb.append("\"uhrung einzelne K\\");
		sb.append("\"aufe}");
		for (Integer i : purchaseIDs) {
			String date = DateHandler.formatDayDateToString(DateHandler.parseNormalDateStringToDate(QueryFactory
						.getSalesday(i)));
			
			sb.append("\\subsection*{Rechnungs-ID: " + i + " am " + date + "}");
			sb.append(FileHandler.readFromFile(TEXTABLEBEGIN));
			List<Integer> l = new ArrayList<Integer>();
			l.add(i);
			sb.append(QueryFactory.getTexTotalPurchase(l, false));
		}

		sb.append("\\end{document}");

		return sb.toString();
	}

	/**
	 * @param operator
	 * @return String containing the LaTeX code for the end of a monthly invoice of a customer
	 */
	private static String createTexInvoiceEnd(Staff operator) {
		StringBuilder sb = new StringBuilder();
		String sig = FileHandler.readFromFile(TEXSIGNATURE);
		sig = sig.replaceFirst("paydate", DateHandler.formatDayDateToString(DateHandler.getPayDate()));
		sig = sig.replaceFirst("operator", operator.getPerson().getFirstName() + " " + operator.getPerson().getLastName() + " (" + operator.getPerson().getRoom() + ")");
		sb.append(sig);

		return sb.toString();
	}

	/**
	 * @param purchaseIDs
	 * @return String containing the LaTeX code for the mid part of a monthly invoice of a customer
	 */
	private static String createTexInvoiceMid(List<Integer> purchaseIDs) {

		StringBuilder sb = new StringBuilder();
		String topic = "\\section*{\\\\\"Ubersicht}";

		String mid = FileHandler.readFromFile(TEXTABLEBEGIN);
		sb.append(topic);
		sb.append(mid);

		sb.append(QueryFactory.getTexTotalPurchase(purchaseIDs, true));

		return sb.toString();
	}

	/**
	 * @param billID
	 * @return String containing the LaTeX code for the header of the monthly invoice of a customer
	 * @throws SQLException 
	 */
	private static String createTexInvoiceHeader(int billID) throws SQLException {

		String str = FileHandler.readFromFile(TEXRECHNUNGHEADER);
		Bill bill = QueryFactory.initializeBill(billID);
		str = str.replaceFirst("customer", bill.getCustomer().getName());
		Customer c = bill.getCustomer();
		str = str.replaceFirst("person", c.getPerson().getFirstName() + " " + c.getPerson().getLastName() + "(" + c.getPerson().getRoom() + ")");
		String invoicePeriod = "";
		Date date = DateHandler.parseMonthDateStringToDate(bill.getDate());
		invoicePeriod = DateHandler.formatHeaderDate(date);

		str = str.replaceFirst("date", invoicePeriod);

		return str;
	}

	/**
	 * @param purchaseIDs
	 * @return String containing the LaTeX code for pure profit
	 */
	private static String createTexPureProfit(List<Integer> purchaseIDs) {
		double profit = QueryFactory.getProfit(purchaseIDs);
		double salary = QueryFactory.getTotalSalary(DateHandler.getMonthBeforeDate(new Date()));
		double operatorSalary = QueryFactory.getSalaryOperator(profit - salary);
		double optionalOperatorSalary = QueryFactory.getOptionalSalaryOperator(profit - salary);

		String strPureProfit = FileHandler.readFromFile(Configuration.TEXPUREPROFIT);
		strPureProfit = strPureProfit.replaceFirst("profit", InvoiceHandler.formatValueToInvoice(profit));
		strPureProfit = strPureProfit.replaceFirst("operatorSalary", InvoiceHandler.formatValueToInvoice(-operatorSalary));
		strPureProfit = strPureProfit.replaceFirst("salary", InvoiceHandler.formatValueToInvoice(-salary));
		strPureProfit = strPureProfit.replaceFirst("optionalOperatorSalary", InvoiceHandler.formatValueToInvoice(-optionalOperatorSalary));
		strPureProfit = strPureProfit.replaceFirst("pureProfit",
				InvoiceHandler.formatValueToInvoice(profit - operatorSalary - salary - optionalOperatorSalary));
		return strPureProfit;
	}

	/**
	 * @param billIDs
	 * @return the LaTeX code for the total revenue of the month.
	 */
	private static String createBalanceTotalPurchase(List<Integer> billIDs) {
		StringBuilder sb = new StringBuilder();
		String header = FileHandler.readFromFile(Configuration.TEXBALANCEHEADER);
		Date invDate = DateHandler.getMonthBeforeDate(new Date());
		
		header = header.replaceFirst("date", DateHandler.formatHeaderDate(invDate));
		sb.append(header);
		sb.append("\\section*{Verk\\\"aufe} \\subsection*{Umsatz}");
		String tableBegin = FileHandler.readFromFile(Configuration.TEXTABLEBEGIN);
		sb.append(tableBegin);

		List<Integer> purchaseIDs = new ArrayList<>();
		for (int i : billIDs) {
			purchaseIDs.addAll(QueryFactory.getPurchaseIDs(i));
		}

		String totalPurchase = QueryFactory.getTexTotalPurchase(purchaseIDs, true);
		sb.append(totalPurchase);
		
		return sb.toString();
	}
	
	/**
	 * Helper method for creating the TeX code for the salary of the operator.
	 * @param beforeProfit
	 * @return
	 */
	private static String createTexSalaryOperator(double beforeProfit) {
		String str = FileHandler.readFromFile(Configuration.TEXTABLEOPERATORSALARY);
		double profitSharing = Double.valueOf(QueryFactory.getProperty(Property.PROFITSHARING));
		double sharedProfit = profitSharing*beforeProfit;
		double operatorSalary = Double.valueOf(QueryFactory.getProperty(Property.OPERATORSALARY));
		double optProfitSharing = Double.valueOf(QueryFactory.getProperty(Property.OPTIONALPROFITSHARING));
		
		
		str = str.replaceFirst("fixSalary", formatValueToInvoice(operatorSalary));
		str = str.replaceFirst("profitsharing", formatValueToInvoice(profitSharing*100));
		str = str.replaceFirst("optprofitsharing", formatValueToInvoice(optProfitSharing*100));
		str = str.replaceFirst("sharedProfit", formatValueToInvoice(sharedProfit));
		str = str.replaceFirst("sum", formatValueToInvoice(sharedProfit+operatorSalary));
		str = str.replaceFirst("optProfit", formatValueToInvoice(optProfitSharing*beforeProfit));
		str = str.replaceAll("profit", formatValueToInvoice(beforeProfit));
		
		return str;
	}

	/**
	 * Returns a String containing the LaTeX code for the end of the inventory invoice.
	 * @param loss
	 * @return
	 */
	private static String createInventoryInvoiceEnd(double loss) {
		String inventoryInvoiceEnd = FileHandler.readFromFile(Configuration.TEXINVENTORYINVOICEEND);
		inventoryInvoiceEnd = inventoryInvoiceEnd.replaceFirst("loss", formatValueToInvoice(loss));
		
		return inventoryInvoiceEnd;
	}
}
